Skip to content

chore: bugfix for two way no merge and better error handling when merging (CM-1117)#4068

Merged
themarolt merged 16 commits intomainfrom
better-error-message-handling-CM-1117
May 6, 2026
Merged

chore: bugfix for two way no merge and better error handling when merging (CM-1117)#4068
themarolt merged 16 commits intomainfrom
better-error-message-handling-CM-1117

Conversation

@themarolt
Copy link
Copy Markdown
Contributor

@themarolt themarolt commented May 4, 2026

Note

Medium Risk
Touches member identity insertion/merge paths and activity processing redirect behavior, which can affect deduplication and data integrity for members. Changes are scoped but involve constraint/merge edge cases and new redirect loops that could mis-route identities if flawed.

Overview
Improves how the data sink worker handles verified identity conflicts by introducing redirect-aware identity insertion and post-merge identity syncing, and by treating stale-prefetch conflicts as no-op successes.

MemberService.create/update can now return a redirect memberId after auto-merge, and ActivityService caches these redirects across the batch (and skips redundant updates, including the actor==objectActor case) to avoid updating already-absorbed rows.

Error metadata for identity conflicts is made more robust (no Postgres detail parsing), includes explicit metadata.errorMessage values for merge/no-merge outcomes, and the cron Slack report now groups errors using error.metadata.errorMessage when present.

findMembersByIdentities now parameterizes the VALUES tuples instead of string-interpolating identity values, improving correctness and safety.

Reviewed by Cursor Bugbot for commit e5b9112. Bugbot is set up for automated code reviews on this repo. Configure here.

…ging

Signed-off-by: Uroš Marolt <uros@marolt.me>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses edge cases in the data sink member-merge flow (especially “two-way” no-merge relationships) and improves observability by attaching more specific error metadata for identity-merge conflicts, which is then used for better error grouping in the cron reporting job.

Changes:

  • Fix no-merge detection to handle bidirectional (A↔B) no-merge rows without throwing.
  • Enhance verified-identity conflict handling by treating “identity already belongs to this member” as a no-op and by setting more explicit metadata.errorMessage values when merges are blocked or fail.
  • Update integration error reporting to group errors by error.metadata.errorMessage when present.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
services/apps/data_sink_worker/src/service/member.service.ts Adjusts no-merge detection to avoid exceptions when multiple matching rows exist (two-way no-merge).
services/apps/data_sink_worker/src/service/activity.service.ts Adds special-case handling for stale prefetch identity conflicts and enriches metadata error messaging around merge attempts.
services/apps/cron_service/src/jobs/integrationResultsReporting.job.ts Groups error breakdown by error.metadata.errorMessage for more actionable aggregation in Slack reports.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Comment thread services/apps/cron_service/src/jobs/integrationResultsReporting.job.ts Outdated
Copilot AI review requested due to automatic review settings May 5, 2026 06:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot AI review requested due to automatic review settings May 5, 2026 08:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Copilot AI review requested due to automatic review settings May 5, 2026 09:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
…tic identity

Signed-off-by: Uroš Marolt <uros@marolt.me>
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot AI review requested due to automatic review settings May 5, 2026 19:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot AI review requested due to automatic review settings May 5, 2026 21:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

services/libs/data-access-layer/src/members/identities.ts:596

  • findMembersByIdentities can generate invalid SQL when called with default args (no memberIdToIgnore and onlyVerified=false): conditions stays empty, so the query becomes WHERE with nothing after it. Consider either adding a guard that pushes a TRUE condition when conditions.length === 0, or conditionally omitting the WHERE clause.
        on mi.platform = i.platform
        and lower(mi.value) = lower(i.value)
        and mi.type = i.type
        and mi."deletedAt" is null
    where ${conditions.join(' and ')}
  `,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
Comment thread services/apps/data_sink_worker/src/service/member.service.ts Outdated
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot AI review requested due to automatic review settings May 6, 2026 05:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

services/libs/data-access-layer/src/members/identities.ts:597

  • findMembersByIdentities can generate invalid SQL when conditions is empty (no memberIdToIgnore and onlyVerified=false): it will render where with nothing after it. Also, if identities is empty, values ${identityParams} becomes values which is invalid. Consider early-returning an empty Map when identities.length === 0 and only adding the WHERE clause when there are conditions (or use where true).
export async function findMembersByIdentities(
  qx: QueryExecutor,
  identities: IMemberIdentity[],
  memberIdToIgnore?: string,
  onlyVerified = false,
): Promise<Map<string, string>> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const params: any = {}

  const conditions: string[] = []
  if (memberIdToIgnore) {
    conditions.push('mi."memberId" <> $(memberIdToIgnore)')
    params.memberIdToIgnore = memberIdToIgnore
  }

  if (onlyVerified) {
    conditions.push('mi.verified = true')
  }

  const identityTuples = identities.map((identity, i) => {
    params[`ip${i}`] = identity.platform
    params[`iv${i}`] = identity.value.trim()
    params[`it${i}`] = identity.type
    return `($(ip${i}), $(iv${i}), $(it${i}))`
  })
  const identityParams = identityTuples.join(', ')

  const result = await qx.select(
    `
    with input_identities (platform, value, type) as (
      values ${identityParams}
    )
    select "memberId", i.platform, i.value, i.type
    from "memberIdentities" mi
      inner join input_identities i
        on mi.platform = i.platform
        and lower(mi.value) = lower(i.value)
        and mi.type = i.type
        and mi."deletedAt" is null
    where ${conditions.join(' and ')}
  `,
    params,

Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Comment thread services/apps/data_sink_worker/src/service/member.service.ts
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 69748ed. Configure here.

Comment thread services/apps/data_sink_worker/src/service/activity.service.ts
Signed-off-by: Uroš Marolt <uros@marolt.me>
@themarolt themarolt merged commit 0058bcd into main May 6, 2026
15 checks passed
@themarolt themarolt deleted the better-error-message-handling-CM-1117 branch May 6, 2026 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants